﻿/*
 * Created by SharpDevelop.
 * User: netcasewqs
 * Date: 2011-1-12
 * Time: 12:33
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NLite.Collections;
using NLite.Log;
using NLite.Reflection;

namespace NLite.Domain.ApplicationServices
{
    /// <summary>
    /// 领域服务接口
    /// </summary>
    public interface IDomainService
    {
    }

    /// <summary>
    /// 可以对领域服务起一个别名
    /// </summary>
    public class ServiceAliasAttribute : Attribute
    {
        /// <summary>
        /// 得到服务别名
        /// </summary>
        public string AliasName { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="aliasName"></param>
        public ServiceAliasAttribute(string aliasName)
        {
            if (string.IsNullOrEmpty(aliasName))
                throw new ArgumentNullException(aliasName);
            AliasName = aliasName.ToLower();
        }
    }


    /// <summary>
    /// 服务请求对象
    /// </summary>
    public class ServiceRequest
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="serviceName"></param>
        /// <param name="actionName"></param>
        /// <param name="data"></param>
        public ServiceRequest(string serviceName, string actionName, object data)
        {
            if (string.IsNullOrEmpty(serviceName))
                throw new ArgumentNullException("serviceName");
            if (string.IsNullOrEmpty(actionName))
                throw new ArgumentNullException("actionName");
            ServiceName = serviceName;
            OperationName = actionName;
            Data = data;
        }

        /// <summary>
        /// 得到服务名
        /// </summary>
        public string ServiceName { get; private set; }
        /// <summary>
        /// 得到服务方法名
        /// </summary>
        public string OperationName { get; private set; }
        /// <summary>
        /// 得到服务方法参数
        /// </summary>
        public object Data { get; private set; }
    }

    /// <summary>
    /// Provides the context for the OperationExecuting method of the OperationFilterAttribute class.
    /// </summary>
    public interface IOperationExecutingContext
    {
        /// <summary>
        /// 得到ServiceRequest
        /// </summary>
        ServiceRequest Request { get; }
        /// <summary>
        /// 得到OperationDescriptor
        /// </summary>
        OperationDescriptor OperationDescriptor { get; }
        /// <summary>
        /// 得到服务方法参数
        /// </summary>
        object[] Arguments { get; }
        /// <summary>
        /// 得到ServiceResponse
        /// </summary>
        ServiceResponse Response { get; set; }
    }

    /// <summary>
    /// Provides the context for the OperationExecuted method of the OperationFilterAttribute class.
    /// </summary>
    public interface IOperationExecutedContext
    {
        /// <summary>
        /// 得到DomainService
        /// </summary>
        IDomainService Service { get; }
        /// <summary>
        /// 得到ServiceRequest
        /// </summary>
        ServiceRequest Request { get; }
        /// <summary>
        /// 得到OperationDescriptor
        /// </summary>
        OperationDescriptor OperationDescriptor { get; }
        /// <summary>
        /// 得到服务方法参数
        /// </summary>
        object[] Arguments { get; }
        /// <summary>
        /// 得到ServiceResponse
        /// </summary>
        ServiceResponse Response { get; set; }
    }

    class OperationExecutedContext : IOperationExecutedContext,IOperationExecutingContext
    {
        public IDomainService Service { get; internal set; }
        public ServiceRequest Request { get; private set; }
        public OperationDescriptor OperationDescriptor { get; private set; }
        public object[] Arguments { get; private set; }
        public ServiceResponse Response { get; set; }

        public OperationExecutedContext(ServiceRequest serviceContext, OperationDescriptor operationDescriptor, object[] args)
        {
            Request = serviceContext;
            OperationDescriptor = operationDescriptor;
            Arguments = args;
        }
    }

   
    /// <summary>
    /// Defines the methods that are used in an operation filter.
    /// </summary>
    public interface IOperationFilter
    {
        /// <summary>
        /// Called after an operation method executes.
        /// </summary>
        /// <param name="ctx"></param>
        void OnOperationExecuted(IOperationExecutedContext ctx);

        /// <summary>
        /// Called before an operation method executes.
        /// </summary>
        /// <param name="ctx"></param>
        void OnOperationExecuting(IOperationExecutingContext ctx);
    }

    /// <summary>
    /// Provides methods that respond to mvc controller requests .
    /// </summary>
    public abstract class DomainService : IDomainService, IOperationFilter
    {
        #region IServiceFilter Members
        /// <summary>
        /// Called after an operation method executes.
        /// </summary>
        /// <param name="ctx"></param>
        public virtual void OnOperationExecuted(IOperationExecutedContext ctx)
        {
        }
        /// <summary>
        /// Called before an operation method executes.
        /// </summary>
        /// <param name="ctx"></param>
        public virtual void OnOperationExecuting(IOperationExecutingContext ctx)
        {
            
        }
        #endregion
    }

    /// <summary>
    /// Encapsulates domain service response information.
    /// </summary>
    public class ServiceResponse:BoolMessage
    {
        /// <summary>
        /// Get ControllerName
        /// </summary>
        public string ControllerName { get; internal set; }
        /// <summary>
        /// Get ActionName
        /// </summary>
        public string ActionName { get; internal set; }
        /// <summary>
        /// Get ActionResultType
        /// </summary>
        public string ActionResultType { get; internal set; }
    }
  
    /// <summary>
    /// Represents an attribute that is used for the name of an opaeration method name.
    /// </summary>
    public class OperationAttribute : Attribute
    {
        static readonly ILog log = LogManager.GetLogger(typeof(OperationAttribute));
        public OperationAttribute()
        {
            ActionResultType = "Default";
        }

        /// <summary>
        /// 得到或设置Operantion Name,如果为空则取方法的名称
        /// </summary>
        public string Alias { get; set; }

        /// <summary>
        /// Get or set action result type in mvc controller action method
        /// </summary>
        public string ActionResultType { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        public bool CheckActionResultType(MethodInfo method)
        {
            if (string.IsNullOrEmpty(ActionResultType))
            {
                log.ErrorFormat("Action result type name is not null for method :[{0}] in service class :[{1}].", method.Name, method.DeclaringType.FullName);
                return false;
            }

            return true;
        }
    }

    /// <summary>
    /// 定义领域服务方法成功执行后，返回给mvc控制器的路由规则
    /// </summary>
    public class NavigationAttribute : Attribute
    {
        /// <summary>
        /// Get or set controller name
        /// </summary>
        public string ControllerName { get; set; }

        /// <summary>
        /// get or set action name
        /// </summary>
        public string ActionName { get; set; }
    }

    /// <summary>
    /// 定义领域服务方法执行失败后，返回给mvc控制器的路由规则
    /// </summary>
    public class ErrorNavigationAttribute : Attribute
    {
        /// <summary>
        /// Get or set controller name
        /// </summary>
        public string ControllerName { get; set; }

        /// <summary>
        /// get or set action name
        /// </summary>
        public string ActionName { get; set; }
        /// <summary>
        /// 得到或设置是否保存模型状态
        /// </summary>
        public bool IsSaveModelState { get; set; }
    }

    /// <summary>
    /// 服务操作元数据描述类
    /// </summary>
    public class OperationDescriptor
    {
        /// <summary>
        /// Get service operation name
        /// </summary>
        public readonly string OperationName;
        /// <summary>
        /// Get service method
        /// </summary>
        public readonly MethodInfo Method;
        /// <summary>
        /// Get ServiceDescriptor
        /// </summary>
        public readonly ServiceDescriptor ServiceDescriptor;

        internal readonly Func<object, object[], object> Executor;

        /// <summary>
        /// Get NavigationAttribute when service method operate successful.
        /// </summary>
        public readonly NavigationAttribute SuccessNavigation;
        /// <summary>
        /// Get ErrorNavigationAttribute when service method operate failed.
        /// </summary>
        public readonly ErrorNavigationAttribute ErrorNavigation;
        /// <summary>
        /// Get OperationFilterAttribute array.
        /// </summary>
        public readonly OperationFilterAttribute[] Filters;
        /// <summary>
        /// Get ActionResultType when service method operate completed.
        /// </summary>
        public readonly string ActionResultType;

        internal OperationDescriptor(MethodInfo method, string operationName,string actionResultType, ServiceDescriptor serviceDescriptor)
        {
            Method = method;
            OperationName = operationName;
            ActionResultType = actionResultType;
            ServiceDescriptor = serviceDescriptor;
            Executor = method.GetFunc<object[], object>();
            SuccessNavigation = method.GetAttribute<NavigationAttribute>(true);
            ErrorNavigation = method.GetAttribute<ErrorNavigationAttribute>(true);
            Filters = method.GetAttributes<OperationFilterAttribute>(true);
        }
    }

    /// <summary>
    /// 服务元数据描述类
    /// </summary>
    public class ServiceDescriptor
    {
        private Map<string, OperationDescriptor> cache = new Map<string, OperationDescriptor>(StringComparer.OrdinalIgnoreCase);

        /// <summary>
        /// Get service type
        /// </summary>
        public readonly Type ServiceType;
        /// <summary>
        /// Get service name
        /// </summary>
        public readonly string ServiceName;


        private IDictionary<string, Pair> Methods;

        struct Pair
        {
            public string ActionResultType;
            public MethodInfo Method;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serviceType"></param>
        /// <param name="serviceName"></param>
        internal ServiceDescriptor(Type serviceType,string serviceName)
        {
            ServiceType = serviceType;
            ServiceName = serviceName;
            ServiceRegistry.Current.Register(new ComponentInfo(serviceType.FullName, serviceType, serviceType, LifestyleFlags.Transient));
            Methods = new Dictionary<string, Pair>();

            var allMethods = (from m in ServiceType
                                        .GetMethods(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public)
                              let attr = m.GetAttribute<OperationAttribute>(true)
                              where attr != null && attr.CheckActionResultType(m)
                              select new { Name = attr.Alias ?? m.Name, ActionResultType = attr.ActionResultType, Method = m });
            foreach (var item in allMethods)
            {
                if (Methods.ContainsKey(item.Name))
                    throw new AmbiguousMatchException(string.Format("Repeat method :[{0}] in service class :[{1}]", item.Name, ServiceType.FullName));
                Methods.Add(item.Name, new Pair { ActionResultType = item.ActionResultType, Method = item.Method });
            }
        }

       

        /// <summary>
        /// Get OperationDescriptor by operation name
        /// </summary>
        /// <param name="operationName"></param>
        /// <returns></returns>
        public OperationDescriptor this[string operationName]
        {
            get
            {
                if (!Methods.ContainsKey(operationName))
                    return null;

                var pair = Methods[operationName];

                return cache.GetOrAdd(operationName, () => new OperationDescriptor(pair.Method, operationName, pair.ActionResultType, this));
            }
        }

       
    }

    /// <summary>
    /// 
    /// </summary>
    public abstract class OperationFilterAttribute : Attribute, IOperationFilter
    {
        #region IServiceFilter Members

        public virtual void OnOperationExecuted(IOperationExecutedContext ctx)
        {
        }

        public virtual void OnOperationExecuting(IOperationExecutingContext ctx)
        {
        }

        #endregion
    }

    /// <summary>
    /// 
    /// </summary>
    public class ServiceBroker
    {
         static readonly ILog log = LogManager.GetLogger(typeof(ServiceBroker));

         static Dictionary<string, ServiceDescriptor> Metadatas;
         static Type ServiceType = typeof(IDomainService);

        static ServiceBroker()
        {
            Metadatas = new Dictionary<string, ServiceDescriptor>();
        }

        /// <summary>
        /// 注册领域控制器
        /// </summary>
        public static void RegisteryServicesFromAssemblyOf<T>()
        {
            typeof(T).Assembly.GetTypes().ForEach(FindService);

        }

        static void FindService(Type t)
        {
            if (t.IsSubclassOf(ServiceType) && t.IsClass && !t.IsAbstract)
            {
                string serviceName = t.Name;
                if (serviceName.EndsWith("Service"))
                {
                    serviceName = serviceName.Substring(0, serviceName.Length - 10).ToLower();
                    Metadatas[serviceName.ToLower()] = new ServiceDescriptor(t, serviceName);
                }

                var alias = t.GetAttribute<ServiceAliasAttribute>(false);
                if(alias != null)
                    Metadatas[alias.AliasName] = new ServiceDescriptor(t, alias.AliasName);
            }
        }

        private void OnAfterAction(IOperationExecutedContext ctx)
        {
            foreach (var filter in ctx.OperationDescriptor.Filters)
                filter.OnOperationExecuted(ctx);

            var filterDomainService = ctx.Service as DomainService;
            if (filterDomainService != null)
                filterDomainService.OnOperationExecuted(ctx);

        }

        private void OnBeforeAction(object service, IOperationExecutingContext ctx)
        {
            foreach (var filter in ctx.OperationDescriptor.Filters)
            {
               filter.OnOperationExecuting(ctx);
                if (ctx.Response != null)
                    return;
            }

            var filterDomainService = service as DomainService;
            if (filterDomainService != null)
                filterDomainService.OnOperationExecuting(ctx);
        }


        public ServiceResponse Execute(ServiceRequest req)
        {
            var resp = OnExecute(req);

            if (resp.Exception != null)
            {
                var nliteEx = resp.Exception as NLiteException;
                if (nliteEx != null)
                {
                    resp.ErrorState.AddRange(nliteEx.ErrorState);
                    if (!nliteEx.ExceptionHandled)
                        nliteEx.Handle();
                }
                else
                    resp.Exception.Handle();
            }

            if (resp.ErrorState.Count > 0)
                resp.ErrorState.ForEach(p => log.Error(p.Message));

            return resp;
        }

        private ServiceResponse OnExecute(ServiceRequest req)
        {
            var resp = new ServiceResponse();

            var serviceName = req.ServiceName;//控制器名
            var operationName = req.OperationName;//ActionName

            if (!Metadatas.ContainsKey(serviceName))
            {
                resp.Success = false;
                resp.ErrorState.AddError(string.Format("Service [{0}] not found!", serviceName));
                return resp;
            }


            //得到领域服务元数据
            var serviceDesc = Metadatas[serviceName];
            //得到领域Action元数据
            var operationDesc = serviceDesc[operationName];

            if (operationDesc == null)
            {
                resp.Success = false;
                resp.ErrorState.AddError(string.Format("Service method [{0}] of [{1}] not found!", operationName, serviceName));
            }

            //设置mvc 的ActionResult 类型
            resp.ActionResultType = operationDesc.ActionResultType;


            //通过DI容器取出对应的领域服务
            IDomainService service = null;
            try
            {
                service = (IDomainService)ServiceLocator.Get(serviceName);
                if (service == null)
                    throw new InvalidOperationException();
            }
            catch (Exception ex)
            {
                resp.Success = false;
                resp.ErrorState.AddError(string.Format("Create service:[{0}] failed.", serviceName));
                resp.Exception = ex;
                return resp;
            }


            try
            {
                //开启工作单元
                using (var uow = UnitOfWork.Start())
                {
                    //创建操作上下文对象
                    var ctx = new OperationExecutedContext(req,operationDesc,new object[] { req.Data});

                    //执行前置过滤器
                    OnBeforeAction(service,ctx);

                    //如果过滤器进行了必要的拦截则返回
                    if (ctx.Response != null)
                        return ctx.Response;

                    //调用领域服务方法
                    resp.Data = operationDesc.Executor(service, ctx.Arguments);

                    ctx.Service = service;

                    //执行后置过滤器
                    OnAfterAction(ctx);

                    //执行成功后， 设置mvc的 controller name 和 ActionName
                    if (operationDesc.SuccessNavigation != null)
                    {
                        resp.ControllerName = operationDesc.SuccessNavigation.ControllerName;
                        resp.ActionName = operationDesc.SuccessNavigation.ActionName;
                    }

                    resp.Success = true;
                }
            }
            catch (Exception ex)
            {
                resp.Success = false;
                resp.Exception = ex;
                resp.ErrorState.AddError(ex.Message);

                //执行失败后， 设置mvc的 controller name 和 ActionName
                if (operationDesc.ErrorNavigation != null)
                {
                    resp.ControllerName = operationDesc.ErrorNavigation.ControllerName;
                    resp.ActionName = operationDesc.ErrorNavigation.ActionName;
                }
            }

            return resp;
        }
    }

    //class User { }

    //[ServiceAlias("Account")]
    //class UserService : IDomainService
    //{
    //    [Operation(Alias="Index")]
    //    [ErrorNavigation(ActionName = "Error",ControllerName = "Home")] 
    //    public IEnumerable<User> List()
    //    {
    //        return new User[0];
    //    }

    //    [Operation]
    //    public User Edit(int userId)
    //    {
    //        return null;
    //    }

    //    [Operation]
    //    [ErrorNavigation(ActionName = "Index")]
    //    public void Update(User user)
    //    {
            
    //    }

    //    [Operation]
    //    [ErrorNavigation(ActionName = "Index")]
    //    public void Add(User user)
    //    {

    //    }

    //    [Operation]
    //    [Navigation(ActionName = "Edit")]
    //    public User Create()
    //    {
    //        return new User();
    //    }

    //    [Operation]
    //    //[Navigation(ActionName = "Index")]
    //    //[ErrorNavigation(ActionName = "Index")]
    //    public void Delete(int userId)
    //    {
            
    //    }

    //}
}
